#ifndef __FS_H__
#define __FS_H__

#include <dirent.h>
#include <fcntl.h>
#include <types.h>
#include <evtloop.h>
#include <threadpool.h>

#if defined(DT_UNKNOWN)
# define HAVE_DIRENT_TYPES
# if defined(DT_REG)
#  define UV__DT_FILE DT_REG
# else
#  define UV__DT_FILE -1
# endif
# if defined(DT_DIR)
#  define UV__DT_DIR DT_DIR
# else
#  define UV__DT_DIR -2
# endif
# if defined(DT_LNK)
#  define UV__DT_LINK DT_LNK
# else
#  define UV__DT_LINK -3
# endif
# if defined(DT_FIFO)
#  define UV__DT_FIFO DT_FIFO
# else
#  define UV__DT_FIFO -4
# endif
# if defined(DT_SOCK)
#  define UV__DT_SOCKET DT_SOCK
# else
#  define UV__DT_SOCKET -5
# endif
# if defined(DT_CHR)
#  define UV__DT_CHAR DT_CHR
# else
#  define UV__DT_CHAR -6
# endif
# if defined(DT_BLK)
#  define UV__DT_BLOCK DT_BLK
# else
#  define UV__DT_BLOCK -7
# endif
#endif

typedef enum
{
    UV_FS_UNKNOWN = -1,
    UV_FS_CUSTOM,
    UV_FS_OPEN,
    UV_FS_CLOSE,
    UV_FS_READ,
    UV_FS_WRITE,
    UV_FS_SENDFILE,
    UV_FS_STAT,
    UV_FS_LSTAT,
    UV_FS_FSTAT,
    UV_FS_FTRUNCATE,
    UV_FS_UTIME,
    UV_FS_FUTIME,
    UV_FS_ACCESS,
    UV_FS_CHMOD,
    UV_FS_FCHMOD,
    UV_FS_FSYNC,
    UV_FS_FDATASYNC,
    UV_FS_UNLINK,
    UV_FS_RMDIR,
    UV_FS_MKDIR,
    UV_FS_MKDTEMP,
    UV_FS_RENAME,
    UV_FS_SCANDIR,
    UV_FS_LINK,
    UV_FS_SYMLINK,
    UV_FS_READLINK,
    UV_FS_CHOWN,
    UV_FS_FCHOWN,
    UV_FS_REALPATH,
    UV_FS_COPYFILE,
    UV_FS_LCHOWN,
    UV_FS_OPENDIR,
    UV_FS_READDIR,
    UV_FS_CLOSEDIR,
    UV_FS_STATFS
} ev_fs_type_t;

typedef struct
{
    long tv_sec;
    long tv_nsec;
} ev_timespec_t;

typedef struct
{
    u64 st_dev;
    u64 st_mode;
    u64 st_nlink;
    u64 st_uid;
    u64 st_gid;
    u64 st_rdev;
    u64 st_ino;
    u64 st_size;
    u64 st_blksize;
    u64 st_blocks;
    u64 st_flags;
    u64 st_gen;
    ev_timespec_t st_atim;
    ev_timespec_t st_mtim;
    ev_timespec_t st_ctim;
    ev_timespec_t st_birthtim;
} ev_stat_t;

typedef enum
{
    UV_DIRENT_UNKNOWN,
    UV_DIRENT_FILE,
    UV_DIRENT_DIR,
    UV_DIRENT_LINK,
    UV_DIRENT_FIFO,
    UV_DIRENT_SOCKET,
    UV_DIRENT_CHAR,
    UV_DIRENT_BLOCK
} uv_dirent_type_t;

typedef struct
{
    const char *name;
    uv_dirent_type_t type;
} uv_dirent_t;

typedef struct
{
    uv_dirent_t *dirents;
    size_t      nentries;
    void        *reserved[4];
    DIR         *dir;
} uv_dir_t;

typedef struct ev_fs_s ev_fs_t;
typedef void (*ev_fs_cb_t)(ev_fs_t *req);
typedef gid_t ev_gid_t;
typedef uid_t ev_uid_t;
typedef struct dirent uv__dirent_t;

struct ev_fs_s
{
    ev_loop_t       *loop;
    ev_fs_type_t    fs_type;
    ev_fs_cb_t      cb;
    struct svc__work work_req;
    ssize_t         result;
    void            *data;
    int             file;
    const char      *path;
    const char      *new_path;
    unsigned int    nbufs;
    ev_buf_t        *bufs;
    ev_buf_t        bufsml[4];

    int             flags;
    off_t           off;
    mode_t          mode;
    union {
        struct {
            ev_uid_t uid;
            ev_gid_t gid;
        };
    };
};

void uv_fs_req_cleanup(ev_fs_t *req);

int uv_fs_close(ev_loop_t *loop, ev_fs_t *req, int file, ev_fs_cb_t cb);
int uv_fs_open(ev_loop_t *loop, ev_fs_t *req, const char *path, int flags, int mode, ev_fs_cb_t cb);
int uv_fs_read(ev_loop_t *loop, ev_fs_t *req, int file, const ev_buf_t bufs[], u32 nbufs, off_t off, ev_fs_cb_t cb);
int uv_fs_write(ev_loop_t *loop, ev_fs_t *req, int file, const ev_buf_t bufs[], u32 nbufs, off_t offset, ev_fs_cb_t cb);
int uv_fs_sendfile(ev_loop_t *loop, ev_fs_t *req, int out_fd, int in_fd, off_t in_offset, size_t length, ev_fs_cb_t cb);

int uv_fs_access(ev_loop_t *loop, ev_fs_t *req, const char *path, int flags, ev_fs_cb_t cb);
int uv_fs_chmod(ev_loop_t *loop, ev_fs_t *req, const char *path, int mode, ev_fs_cb_t cb);
int uv_fs_chown(ev_loop_t *loop, ev_fs_t *req, const char *path, ev_uid_t uid, ev_gid_t gid, ev_fs_cb_t cb);
int uv_fs_fchmod(ev_loop_t *loop, ev_fs_t *req, int file, int mode, ev_fs_cb_t cb);
int uv_fs_fchown(ev_loop_t *loop, ev_fs_t *req, int file, ev_uid_t uid, ev_gid_t gid, ev_fs_cb_t cb);
int uv_fs_fsync(ev_loop_t *loop, ev_fs_t *req, int file, ev_fs_cb_t cb);
int uv_fs_ftruncate(ev_loop_t *loop, ev_fs_t *req, int file, off_t off, ev_fs_cb_t cb);
int uv_fs_mkdir(ev_loop_t *loop, ev_fs_t *req, const char *path, int mode, ev_fs_cb_t cb);
int uv_fs_realpath(ev_loop_t *loop, ev_fs_t *req, const char *path, ev_fs_cb_t cb);
int uv_fs_rename(ev_loop_t *loop, ev_fs_t *req, const char *path, const char *new_path, ev_fs_cb_t cb);
int uv_fs_rmdir(ev_loop_t *loop, ev_fs_t *req, const char *path, ev_fs_cb_t cb);
int uv_fs_stat(ev_loop_t *loop, ev_fs_t *req, const char *path, ev_fs_cb_t cb);
int uv_fs_symlink(ev_loop_t *loop, ev_fs_t *req, const char *path, const char *new_path, int flags, ev_fs_cb_t cb);
int uv_fs_unlink(ev_loop_t *loop, ev_fs_t *req, const char *path, ev_fs_cb_t cb);
#endif
